package twitter

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"github.com/go-resty/resty/v2"
	"github.com/google/uuid"
	"github.com/zeromicro/go-zero/core/syncx"
	"net/http"
	"net/url"
	"sieve_engine/execute"
	"sieve_engine/lib/config"
	"sieve_engine/lib/log"
	"sieve_engine/task"
	"sieve_engine/utils"
	"strconv"
	"strings"
	"sync"
	"time"
)

type twitterExecutor struct {
	*execute.DefaultExecutor
	execute.DefaultNetWorkConfig
}

var (
	twExecutor execute.TaskExecutorInterface = &twitterExecutor{
		DefaultExecutor: &execute.DefaultExecutor{
			Debug:                *config.Bool("sieve.twitter.debug", true, "debug"),
			PrintMore:            *config.Bool("sieve.twitter.print_more", true, "print more"),
			ProxyEnable:          *config.Bool("sieve.twitter.proxy.enable", true, "proxy enable"),
			ProxyDebug:           *config.Bool("sieve.twitter.proxy.debug", true, "proxy debug"),
			NetworkErrRetryCount: *config.Int("sieve.twitter.proxy.retry_count_if_network_error", -1, "retry count if network error"),
			DefaultResultCache:   execute.NewDefaultResultCache(),
		},
	}
	fetchingToken = syncx.ForAtomicBool(false)
)

const (
	twitterOauth2Token  = "https://api.twitter.com/oauth2/token"
	twitterGuestToken   = "https://api.twitter.com/1.1/guest/activate.json"
	twitterFlowTokenUrl = "https://api.twitter.com/1.1/onboarding/task.json?flow_name=login&api_version=1&known_device_token=&sim_country_code=us"
)

func init() {
	execute.Register("twitter", twExecutor)
}

var (
	delChannel     = make(chan []string, 256)
	tokenCacheOnce = sync.Once{}
	tokenCache     = struct {
		cache  map[string]*TokenData
		locker sync.Mutex
	}{
		cache: make(map[string]*TokenData),
	}
)

func getTokenCacheSize() int {
	tokenCache.locker.Lock()
	defer tokenCache.locker.Unlock()

	return len(tokenCache.cache)
}

func tryLock() bool {

	var (
		fetchCount = 3
		b          bool
	)
	for fetchCount > 0 {
		b = tokenCache.locker.TryLock()
		if !b {
			time.Sleep(time.Millisecond * 2)
			fetchCount = fetchCount - 1
			continue
		} else {
			break
		}
	}

	if !b {
		return false
	}

	return true
}

func getTokenCache() *TokenData {

	tokenCacheOnce.Do(func() {
		go func() {
			for v := range delChannel {

				for _, vv := range v {
					if tryLock() {
						if len(v) == len(tokenCache.cache) {
							tokenCache.cache = make(map[string]*TokenData)
							tokenCache.locker.Unlock()
							break
						}
						delete(tokenCache.cache, vv)
						tokenCache.locker.Unlock()
					}
				}
			}
		}()
	})

	if !tryLock() {
		return nil
	}

	defer tokenCache.locker.Unlock()

	var r *TokenData
	if len(tokenCache.cache) == 0 {
		return nil
	}

	var (
		now            = time.Now()
		deleteElements []string
	)

	for _, v := range tokenCache.cache {

		/* token 已经失效 */
		if (v.XRateLimitReset > 0 && v.XRateLimitLimit > 0 && (now.Unix() > int64(v.XRateLimitReset) || v.XRateLimitRemaining <= 0)) || v.IsTokenInvalid.True() {
			deleteElements = append(deleteElements, v.XClientUUID)
		} else {
			if !v.IsRefreshToken.True() {
				if v.IsBadGuestToken.True() || v.IsExpiredFlowToken.True() {
					v.IsRefreshToken.Set(true)
					tokenCache.cache[v.XClientUUID] = v
				}
				r = v
				break
			}
		}
	}

	if len(deleteElements) > 0 {
		select {
		case delChannel <- deleteElements:
			break
		default:
			break
		}
	}
	return r
}

func setOrUpdateTokenCache(token *TokenData) {

	tokenCache.locker.Lock()
	defer tokenCache.locker.Unlock()

	// tokenCache.cache[token.Authorization] = token
	tokenCache.cache[token.XClientUUID] = token
}

func (s *twitterExecutor) Execute(params execute.TaskExecutorParams, taskProgress *task.Result) error {

	return s.DefaultExecutor.Execute(params, taskProgress, s.getApi)
}

func (s *twitterExecutor) getNetWorkConfig() *execute.NetWorkConfig {

	def := execute.GetSycTransportSetting()
	def.DialTimeOut = 1 * time.Second
	def.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
	def.IdleConnTimeout = time.Second * 90
	def.ExpectContinueTimeout = time.Second * 10
	def.DisableKeepAlives = false

	return &execute.NetWorkConfig{
		TransPortConfig: def,
		HttpConfig: execute.HttpConfig{
			Timeout: time.Second * 60,
		},
	}
}

func (s *twitterExecutor) getApi(phone string, params execute.TaskExecutorParams,
	roundTripper http.RoundTripper, reuseProxy bool) (http.RoundTripper, bool, *url.URL, error) {

	var (
		twitterTokenResp = TokenResp{}
		tokenData        = NewTwitterTokenData()
		guestToken       = GuestTokenResp{}
		attAndFlowToken  = AttAndFlowTokenResp{}
		retryCount       = s.NetworkErrRetryCount // 失败这个多次就设置为失败(筛以有效设置为invalid?) -1 表示一直重试
		step1RetryCount  = retryCount
		step2RetryCount  = retryCount
		step3RetryCount  = retryCount
		tokenFromCache   bool
	)

	if t := getTokenCache(); t != nil {
		tokenFromCache = true
		tokenData = t
	} else {
		tokenData.IsRefreshToken.Set(true)
	}

	if s.PrintMore {
		log.Info(fmt.Sprintf("缓存的 token 数量有:%v", getTokenCacheSize()))
	}

	if !tokenFromCache {
		for i := 0; i < step1RetryCount || step1RetryCount == -1; i++ {

			if rt, b, pUrl, err := s.step1(params, roundTripper, reuseProxy, &twitterTokenResp); err != nil {

				reuseProxy = false
				sleepDuration := time.Millisecond * 3
				if s.PrintMore {
					log.Err(fmt.Sprintf("当前(任务编号:%v,批次号:%v ,号码:%v),使用代理:(%v) ,执行任务(第%v次 )失败:%v ,获取的token信息(缓存获取:%v %v),休息%v后继续执行",
						params.TaskInfo.Id, params.BatchIndex, phone, pUrl, i+1, err, tokenFromCache, tokenData.ToString(), sleepDuration))
				}
				if i == step1RetryCount-1 {
					return rt, b, pUrl, err
				}
				time.Sleep(sleepDuration)
				continue
			} else {
				tokenData.Cookie = twitterTokenResp.CookieString
				auth := fmt.Sprintf("%v %s", twitterTokenResp.TokenType, twitterTokenResp.AccessToken)
				tokenData.Authorization = auth
				tokenData.IsTokenInvalid.Set(false)
				setOrUpdateTokenCache(tokenData)
				break
			}
		}
	}

	if !tokenFromCache || tokenData.IsBadGuestToken.True() {
		reuseProxy = false
		for i := 0; i < step2RetryCount || step2RetryCount == -1; i++ {

			if rt, b, pUrl, err := s.step2(params, roundTripper, reuseProxy, tokenData, &guestToken); err != nil {

				reuseProxy = false
				sleepDuration := time.Millisecond * 3
				if s.PrintMore {
					log.Err(fmt.Sprintf("当前(任务编号:%v,批次号:%v ,号码:%v),使用代理:(%v) ,执行任务(第%v次 )失败:%v ,获取的token信息(缓存获取:%v %v),休息%v后继续执行",
						params.TaskInfo.Id, params.BatchIndex, phone, pUrl, i+1, err, tokenFromCache, tokenData.ToString(), sleepDuration))
				}

				if i == step2RetryCount-1 {
					return rt, b, pUrl, err
				}

				time.Sleep(sleepDuration)
				continue
			} else {
				tokenData.IsTokenInvalid.Set(false)
				tokenData.IsBadGuestToken.Set(false)
				tokenData.IsRefreshToken.Set(false)
				tokenData.XGuestToken = guestToken.GuestToken
				setOrUpdateTokenCache(tokenData)
				break
			}
		}
	}

	// reuseProxy = false
	for i := 0; i < step3RetryCount || step3RetryCount == -1; i++ {

		if rt, b, pUrl, err := s.step3(params, roundTripper, reuseProxy, tokenData, &attAndFlowToken); err != nil {
			reuseProxy = false
			sleepDuration := time.Millisecond * 3
			if s.PrintMore {
				log.Err(fmt.Sprintf("当前(任务编号:%v,批次号:%v ,号码:%v),使用代理:(%v) ,执行任务(第%v次 )失败:%v ,获取的token信息(缓存获取:%v %v),休息%v后继续执行",
					params.TaskInfo.Id, params.BatchIndex, phone, pUrl, i+1, err, tokenFromCache, tokenData.ToString(), sleepDuration))
			}

			if i == step3RetryCount-1 {
				return rt, b, pUrl, err
			}

			time.Sleep(sleepDuration)
			continue
		} else {
			tokenData.IsTokenInvalid.Set(false)
			tokenData.IsExpiredFlowToken.Set(false)
			break
		}
	}

	defer func() {
		tokenData.IsTokenInvalid.Set(false)
		tokenData.IsBadGuestToken.Set(false)
		tokenData.IsExpiredFlowToken.Set(false)
		setOrUpdateTokenCache(tokenData)
	}()

	if s.PrintMore {
		log.Info(fmt.Sprintf("当前(任务编号:%v,批次号:%v,号码:%v),获取的token信息(缓存获取:%v %v)",
			params.TaskInfo.Id, params.BatchIndex, phone, tokenFromCache, tokenData.ToString()))
	}

	// reuseProxy = false
	return s.step4(phone, params, roundTripper, reuseProxy, tokenData, &attAndFlowToken)
}

func (s *twitterExecutor) step1(params execute.TaskExecutorParams, roundTripper http.RoundTripper,
	reuseProxy bool, twitterTokenResp *TokenResp) (http.RoundTripper, bool, *url.URL, error) {

	var (
		client   = resty.New()
		proxyURL *url.URL
	)
	if !reuseProxy {
		/* 如果不带用代理复用策略，那么就重新获取代理 */
		roundTripper, proxyURL = execute.SetDefaultTransport("", s.getNetWorkConfig())
	}

	client.SetTransport(roundTripper).SetTimeout(s.getNetWorkConfig().HttpConfig.Timeout)
	step1AddHeader(client.Header, map[string][]string{
		"X-Client-UUID":             {utils.RandomString(16)},
		"X-Twitter-Client-DeviceID": {uuid.NewString()},
		"Authorization":             {"Basic M25WdVNvQlpueDZVNHZ6VXhmNXc6QmNzNTlFRmJic2RGNlNsOU5nNzFzbWdTdFdFR3dYWEtTall2UFZ0N3F5cw=="},
		"Content-Type":              {"application/x-www-form-urlencoded"},
		"X-B3-TraceId":              {utils.RandomString(16)},
	})
	resp, err := client.SetDebug(s.ProxyDebug).R().SetBody("grant_type=client_credentials").
		Post(fmt.Sprintf("%v", twitterOauth2Token))
	if err != nil {
		if s.Debug {
			log.Err(fmt.Sprintf("任务ID:%v 任务批次:%v 请求接口错误:%v", params.TaskInfo.Id, params.BatchIndex, err))
		}

		return roundTripper, false, proxyURL, err
	}

	err = json.Unmarshal(resp.Body(), twitterTokenResp)
	if err != nil {
		return roundTripper, false, proxyURL, err
	}

	var cookieString string
	for _, c := range resp.RawResponse.Header.Values("Set-Cookie") {
		length := strings.Index(c, ";")
		subCookie := c[0:length]
		cookieString = cookieString + subCookie + ";"
	}
	twitterTokenResp.CookieString = cookieString

	return roundTripper, true, proxyURL, nil
}

func (s *twitterExecutor) step2(params execute.TaskExecutorParams, roundTripper http.RoundTripper,
	reuseProxy bool, twitterTokenResp *TokenData, guestToken *GuestTokenResp) (http.RoundTripper, bool, *url.URL, error) {

	var (
		client   = resty.New()
		proxyURL *url.URL
	)

	if !reuseProxy {
		/* 如果不带用代理复用策略，那么就重新获取代理 */
		roundTripper, proxyURL = execute.SetDefaultTransport("", s.getNetWorkConfig())
	}

	client.SetTransport(roundTripper).SetTimeout(s.getNetWorkConfig().HttpConfig.Timeout)
	step2AddHeader(client.Header, map[string][]string{
		"X-Twitter-Client-AdID":              {twitterTokenResp.XTwitterClientAdID},
		"X-Client-UUID":                      {twitterTokenResp.XClientUUID},
		"X-Twitter-Client-DeviceID":          {twitterTokenResp.XTwitterClientDeviceID},
		"Authorization":                      {twitterTokenResp.Authorization},
		"X-B3-TraceId":                       {utils.RandomString(16)},
		"Cookie":                             {twitterTokenResp.Cookie},
		"X-Twitter-Client-Limit-Ad-Tracking": {"0"},
	})
	resp, err := client.SetDebug(s.ProxyDebug).R().
		Post(fmt.Sprintf("%v", twitterGuestToken))
	if err != nil {
		if s.PrintMore {
			log.Err(fmt.Sprintf("任务ID:%v 任务批次:%v 请求接口错误:%v", params.TaskInfo.Id, params.BatchIndex, err))
		}
		s.dealTokenInvalid(err, twitterTokenResp)
		return roundTripper, false, proxyURL, err
	}

	err = json.Unmarshal(resp.Body(), guestToken)
	if err != nil {
		return roundTripper, false, proxyURL, err
	}

	return roundTripper, false, proxyURL, nil
}

func (s *twitterExecutor) step3(params execute.TaskExecutorParams, roundTripper http.RoundTripper,
	reuseProxy bool, tokenData *TokenData, attAndFlowToken *AttAndFlowTokenResp) (http.RoundTripper, bool, *url.URL, error) {

	var (
		client   = resty.New()
		proxyURL *url.URL
	)
	if !reuseProxy {
		/* 如果不带用代理复用策略，那么就重新获取代理 */
		roundTripper, proxyURL = execute.SetDefaultTransport("", s.getNetWorkConfig())
	}

	numberFlowToken := FlowTokenInfo
	numberFlowTokenData, _ := json.Marshal(numberFlowToken)
	client.SetTransport(roundTripper).SetTimeout(s.getNetWorkConfig().HttpConfig.Timeout)
	step3AddHeader(client.Header, map[string][]string{
		"Content-Type":                       {"application/json"},
		"Cookie":                             {tokenData.Cookie},
		"OS-Version":                         {"29"},
		"Authorization":                      {tokenData.Authorization},
		"System-User-Agent":                  {"Dalvik/2.1.0 (Linux; U; Android 10; Pixel 3 Build/QQ3A.200805.001)"},
		"Twitter-Display-Size":               {"1080x2028x440"},
		"X-Guest-Token":                      {tokenData.XGuestToken},
		"X-B3-TraceId":                       {utils.RandomString(16)},
		"X-Client-UUID":                      {tokenData.XClientUUID},
		"X-Twitter-Client-AdID":              {tokenData.XTwitterClientAdID},
		"X-Twitter-Client-AppSetID":          {tokenData.XTwitterClientAppSetID},
		"X-Twitter-Client-DeviceID":          {tokenData.XTwitterClientDeviceID},
		"X-Twitter-Active-User":              {"yes"},
		"X-Twitter-Client-Limit-Ad-Tracking": {"0"},
	})
	resp, err := client.SetDebug(s.ProxyDebug).R().SetBody(string(numberFlowTokenData)).
		Post(fmt.Sprintf("%v", twitterFlowTokenUrl))
	if err != nil {
		if s.PrintMore {
			log.Err(fmt.Sprintf("任务ID:%v 任务批次:%v 请求接口错误:%v", params.TaskInfo.Id, params.BatchIndex, err))
		}
		s.dealTokenInvalid(err, tokenData)
		return roundTripper, false, proxyURL, err
	}

	var flowResp = &FlowTokenResp{}
	if err = json.Unmarshal(resp.Body(), flowResp); err != nil {
		return roundTripper, false, proxyURL, err
	}

	att := ""
	attHeader := resp.RawResponse.Header.Values("att")
	if len(attHeader) > 0 {
		att = attHeader[0]
	}

	// 初始化含有token 以及请求头att
	attAndFlowToken.FlowToken = flowResp.FlowToken
	attAndFlowToken.Status = flowResp.Status
	attAndFlowToken.Att = att

	return roundTripper, true, proxyURL, nil
}

func (s *twitterExecutor) step4(phone string, params execute.TaskExecutorParams, roundTripper http.RoundTripper,
	reuseProxy bool, tokenData *TokenData, flowCh *AttAndFlowTokenResp) (http.RoundTripper, bool, *url.URL, error) {

	var (
		client        = resty.New()
		proxyURL      *url.URL
		guestTokenUrl = "https://api.twitter.com/1.1/onboarding/task.json"
	)
	if !reuseProxy {
		/* 如果不带用代理复用策略，那么就重新获取代理 */
		roundTripper, proxyURL = execute.SetDefaultTransport("", s.getNetWorkConfig())
	}

	numberFlowToken := NumberVerifyInfo
	// 获取返回信息
	numberFlowToken.FlowToken = flowCh.FlowToken
	numberFlowToken.SubtaskInputs[0].EnterText.Text = phone
	numberFlowToken.SubtaskInputs[0].EnterText.SuggestionId = nil
	numberVerifyData, _ := json.Marshal(numberFlowToken)
	client.SetTransport(roundTripper).SetTimeout(s.getNetWorkConfig().HttpConfig.Timeout)

	step4AddHeader(client.Header, map[string][]string{
		"OS-Version":                         {"29"},
		"att":                                {flowCh.Att},
		"Authorization":                      {tokenData.Authorization},
		"Cookie":                             {tokenData.Cookie},
		"Content-Length":                     {strconv.Itoa(len(numberVerifyData))},
		"Content-Type":                       {"application/json; charset=utf-8"},
		"System-User-Agent":                  {"Dalvik/2.1.0 (Linux; U; Android 10; Pixel 3 Build/QQ3A.200805.001"},
		"Twitter-Display-Size":               {"1080x2028x440"},
		"X-Guest-Token":                      {tokenData.XGuestToken},
		"X-B3-TraceId":                       {utils.RandomString(16)},
		"X-Client-UUID":                      {tokenData.XClientUUID},
		"X-Twitter-Client-AdID":              {tokenData.XTwitterClientAdID},
		"X-Twitter-Client-AppSetID":          {tokenData.XTwitterClientAppSetID},
		"X-Twitter-Client-DeviceID":          {tokenData.XTwitterClientDeviceID},
		"X-Twitter-Active-User":              {"yes"},
		"X-Twitter-Client-Limit-Ad-Tracking": {"0"},
	})
	resp, err := client.SetDebug(s.ProxyDebug).R().SetBody(string(numberVerifyData)).
		Post(fmt.Sprintf("%v", guestTokenUrl))
	if err != nil {
		if s.PrintMore {
			log.Err(fmt.Sprintf("任务ID:%v 任务批次:%v 请求接口错误:%v", params.TaskInfo.Id, params.BatchIndex, err))
		}
		s.dealTokenInvalid(err, tokenData)
		return roundTripper, false, proxyURL, err
	}

	respHeader := resp.Header()

	// X-Rate-Limit-Remaining
	rateLimitRemaining := respHeader.Get("X-Rate-Limit-Remaining")
	tokenData.XRateLimitRemaining, _ = strconv.Atoi(rateLimitRemaining)

	xRateLimitLimit := respHeader.Get("X-Rate-Limit-Limit")
	tokenData.XRateLimitLimit, _ = strconv.Atoi(xRateLimitLimit)

	xRateLimitReset := respHeader.Get("X-Rate-Limit-Reset")
	tokenData.XRateLimitReset, _ = strconv.Atoi(xRateLimitReset)

	b, err := parseResult(resp.Body(), tokenData)
	return roundTripper, b, proxyURL, err
}

func parseResult(body []byte, tokenData *TokenData) (bool, error) {

	twitterResp := NumberVerifyResp{}
	err := json.Unmarshal(body, &twitterResp)
	if err != nil {
		return false, execute.RequestErrorNetError
	}

	if twitterResp.Status == "success" { // success
		subtaskId := twitterResp.Subtasks[0].SubtaskId
		// 号码存在 || 账号存在（但是提示账号存在异常登录活动）
		if subtaskId == "LoginEnterPassword" || subtaskId == "LoginEnterAlternateIdentifierSubtask" { // LoginEnterPassword
			return true, nil
		}
		// 频率过快，需要切换ip
		if subtaskId == "DenyLoginSubtask" {
			return false, execute.RequestErrorRateLimitExceeded
		}
	}

	// 没有找到账号：
	// {"errors":[{"code":399,"message":"抱歉，我们无法找到你的账号。"}]}
	// {"errors":[{"code":399,"message":"Sorry, we could not find your account."}]}
	if twitterResp.Errors[0].Code == 399 && (strings.Contains(twitterResp.Errors[0].Message, "Sorry, we could not find your account.")) { // Sorry, we could not find your account.
		return false, nil
	}

	if twitterResp.Errors[0].Code == 366 && (strings.Contains(twitterResp.Errors[0].Message, "flow token not recognized Invalid flow token")) { // flow token not recognized Invalid flow token
		tokenData.IsExpiredFlowToken.Set(true)
		return false, execute.RequestErrorBadFlowToken
	}

	// Guest_token过期：
	// {"errors":[{"message":"Bad guest token","code":239}]}
	if twitterResp.Errors[0].Code == 239 && (strings.Contains(twitterResp.Errors[0].Message, "Bad guest token")) {
		tokenData.IsBadGuestToken.Set(true)
		return false, execute.RequestErrorBadGuestToken
	}

	// 缺少前面获取的参数去请求时
	// 这个token失效，需要丢弃
	// {"errors":[{"code":348,"message":"Client application is not permitted to access this api."}]}
	if twitterResp.Errors[0].Code == 348 && (strings.Contains(twitterResp.Errors[0].Message, "Client application is not permitted to access this api.")) {
		tokenData.IsTokenInvalid.Set(true)
		return false, execute.RequestErrorBadToken
	}

	// flow_token被消费需要重新获取
	// {"errors":[{"code":366,"message":"flow name LoginFlow is currently not accessible"}]}
	if twitterResp.Errors[0].Code == 366 && strings.Contains(twitterResp.Errors[0].Message, "flow name LoginFlow is currently not accessible") {
		tokenData.IsExpiredFlowToken.Set(true)
		return false, execute.RequestErrorBadFlowToken
	}

	return false, execute.RequestErrorCanceledError
}

func (s *twitterExecutor) dealTokenInvalid(err error, tokenData *TokenData) bool {
	/*
		remote error: tls: unexpected message  183
		remote error: tls: protocol version not supported 61
		remote error: tls: bad record MAC 70
		tls: server did not echo the legacy session ID  307
	*/
	if strings.LastIndex(err.Error(), "tls:") != -1 {
		tokenData.IsTokenInvalid.Set(true)
		return true
	}

	return false
}

func step4AddHeader(header http.Header, headers map[string][]string) {
	header.Add("Accept", "application/json")
	header.Add("Accept-Language", "en-US")
	header.Add("Accept-Encoding", "br, gzip, deflate")
	header.Add("Cache-Control", "no-store")
	header.Add("Host", "api.twitter.com")
	header.Add("OS-Security-Patch-Level", "2020-08-05")
	header.Add("Optimize-Body", "true")
	header.Add("Timezone", "America/New_York")
	header.Add("User-Agent", "TwitterAndroid/9.75.0-release.0 (29750000-r-0) Pixel+3/10 (Google;Pixel+3;google;blueline;0;;1;2015)")

	header.Add("X-Twitter-Active-User", "no")
	header.Add("X-Twitter-API-Version", "5")
	header.Add("X-Twitter-Client", "TwitterAndroid")
	header.Add("X-Twitter-Client-Language", "en-US")
	header.Add("X-Twitter-Client-Version", "9.75.0-release.0")
	header.Add("X-Twitter-Client-Flavor", "")

	// header.Add("Connection", "Keep-Alive")
	header.Add("Connection", "close")
	header.Add("Proxy-Connection", "close")
	// request.Header.Add("Proxy-Authenticate", "auth")
	// request.Header.Add("Proxy-Authorization", "auth")
	for k, v := range headers {
		header[k] = v
	}
}

func step3AddHeader(header http.Header, headers map[string][]string) {
	header.Add("Accept", "application/json")
	header.Add("Accept-Language", "en-US")
	header.Add("Accept-Encoding", "br, gzip, deflate")
	header.Add("Cache-Control", "no-store")
	header.Add("Host", "api.twitter.com")
	header.Add("OS-Security-Patch-Level", "2020-08-05")
	header.Add("Optimize-Body", "true")
	header.Add("Timezone", "America/New_York")
	header.Add("User-Agent", "TwitterAndroid/9.75.0-release.0 (29750000-r-0) Pixel+3/10 (Google;Pixel+3;google;blueline;0;;1;2015)")

	header.Add("X-Twitter-Active-User", "no")
	header.Add("X-Twitter-API-Version", "5")
	header.Add("X-Twitter-Client", "TwitterAndroid")
	header.Add("X-Twitter-Client-Language", "en-US")
	header.Add("X-Twitter-Client-Version", "9.75.0-release.0")
	header.Add("X-Twitter-Client-Flavor", "")

	// header.Add("Connection", "Keep-Alive")
	header.Add("Connection", "close")
	header.Add("Proxy-Connection", "close")
	// request.Header.Add("Proxy-Authenticate", "auth")
	// request.Header.Add("Proxy-Authorization", "auth")
	for k, v := range headers {
		header[k] = v
	}
}

func step2AddHeader(header http.Header, headers map[string][]string) {
	header.Add("Accept", "application/json")
	header.Add("Accept-Language", "en-US")
	header.Add("Accept-Encoding", "br, gzip, deflate")
	header.Add("Cache-Control", "no-store")
	header.Add("Host", "api.twitter.com")
	header.Add("OS-Security-Patch-Level", "2020-08-05")
	header.Add("Optimize-Body", "true")
	header.Add("Timezone", "America/New_York")
	header.Add("User-Agent", "TwitterAndroid/9.75.0-release.0 (29750000-r-0) Pixel+3/10 (Google;Pixel+3;google;blueline;0;;1;2015)")

	header.Add("X-Twitter-Active-User", "no")
	header.Add("X-Twitter-API-Version", "5")
	header.Add("X-Twitter-Client", "TwitterAndroid")
	header.Add("X-Twitter-Client-Language", "en-US")
	header.Add("X-Twitter-Client-Version", "9.75.0-release.0")
	header.Add("X-Twitter-Client-Flavor", "")

	// header.Add("Connection", "Keep-Alive")
	header.Add("Connection", "close")
	header.Add("Proxy-Connection", "close")
	// request.Header.Add("Proxy-Authenticate", "auth")
	// request.Header.Add("Proxy-Authorization", "auth")
	for k, v := range headers {
		header[k] = v
	}
}

func step1AddHeader(header http.Header, headers map[string][]string) {
	header.Add("Accept", "application/json")
	header.Add("Accept-Language", "en-US")
	header.Add("Accept-Encoding", "br, gzip, deflate")
	header.Add("Cache-Control", "no-store")
	header.Add("Host", "api.twitter.com")
	header.Add("OS-Security-Patch-Level", "2020-08-05")
	header.Add("Optimize-Body", "true")
	header.Add("Timezone", "America/New_York")
	header.Add("User-Agent", "TwitterAndroid/9.75.0-release.0 (29750000-r-0) Pixel+3/10 (Google;Pixel+3;google;blueline;0;;1;2015)")

	header.Add("X-Twitter-Active-User", "no")
	header.Add("X-Twitter-API-Version", "5")
	header.Add("X-Twitter-Client", "TwitterAndroid")
	header.Add("X-Twitter-Client-Language", "en-US")
	header.Add("X-Twitter-Client-Version", "9.75.0-release.0")
	header.Add("X-Twitter-Client-Flavor", "")

	// request.Header.Add("Connection", "Keep-Alive")
	header.Add("Connection", "close")
	header.Add("Proxy-Connection", "close")
	// request.Header.Add("Proxy-Authenticate", "auth")
	// request.Header.Add("Proxy-Authorization", "auth")
	for k, v := range headers {
		header[k] = v
	}
}
